package com.straw.hat.com.straw.hat.jdk.concurrent.aqs;

import sun.misc.Unsafe;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 *
 *
 *AQS有点类似你去医院看门诊取号排队的过程
 *
 * 1.先去取号机上获取号码，如果获取到号码能立马看医生,就直接去去医生那里
 * 2.但是如果取到的号码前已经有人在看医生了,那你就要在候诊区等待
 * 3.这个时候其实你就可以休息了，只需要你前面的人在他就诊结束出来告知你就好了
 *
 * AQS主要继承AbstractQueuedSynchronizer实现其中的功能
 *
 * 他的内部维护了一个state和FIFO的队列来实现锁的概念
 *
 * 操作状态的方法他提供getState/setState/compareAndSetState/
 *
 * 对于资源的操作AQS规定两种模式 独占/共享  tryAcquire/tryAcquiredShared
 *
 * AQS = park+自选
 * @company: 鲸灵科技
 * @author: qiyue@webuy.ai
 * @date: 2019/9/29
 * @time: 10:58 下午
 */
public class AqsStudy  {


    private static final Unsafe UNSAFE = Unsafe.getUnsafe();

    private volatile Node head;
    private volatile Node tail;

    private static final long headOffset;
    private static final long tailOffset;

    static {
        try {
            headOffset = UNSAFE.objectFieldOffset
                    (AqsStudy.class.getDeclaredField("head"));
            tailOffset = UNSAFE.objectFieldOffset
                    (AqsStudy.class.getDeclaredField("tail"));

        } catch (Exception ex) { throw new Error(ex); }
    }



    private volatile int state = 0;

    private static volatile long stateOffset = 0l;
    static {
        try {
            stateOffset = UNSAFE.objectFieldOffset
                    (AqsStudy.class.getDeclaredField("state"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile Thread ownerThread  = null;

    public Node getHead() {
        return head;
    }

    public void setHead(Node head) {
        this.head = head;
    }

    public Node getTail() {
        return tail;
    }

    public void setTail(Node tail) {
        this.tail = tail;
    }

    public Thread getOwnerThread() {
        return ownerThread;
    }

    public void setOwnerThread(Thread ownerThread) {
        this.ownerThread = ownerThread;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public boolean lock() {

        int state = getState();
        Thread currentThread = Thread.currentThread();

        /*判断锁有没有被获取*/
        if (state == 0) {
            if (UNSAFE.compareAndSwapInt(this,stateOffset,0,1)){
                setOwnerThread(currentThread);
                return true;
            }
        } else {
            int next = state +1 ;
            if (next < 0)
                throw new Error("Maximum lock count exceeded");
            /*重入锁的原理*/
            if (getOwnerThread() == currentThread && UNSAFE.compareAndSwapInt(this,stateOffset,state,next)) {
                    return true;
            } else {
                /*添加到等待队列*/
                Node node  = new Node(currentThread, LockModel.MONOPOLY.getNum());
                addWaits(node);
            }
        }
        return false;
    }


    private Node addWaits(Node node) {
        /*判断创建队列*/
        /*把当前节点添加到队列的尾部*/
        Node t = getTail();
        if (t != null && compareAndSetTail(t,node)) {
            t.next = node;
            node.pre = t;
            return t;
        }
        enq(node);
        return node;
    }

    private Node enq(Node node) {
        for (;;) {
            /*初始化队列,创建一个空节点(AQS 认为队首是持有锁的线程,这个时候锁已经被其他线程持有了，所以创建一个空节点来代替)*/
            Node t = getTail();
            if (t == null && compareAndSetHead(new Node())) {
                compareAndSetTail(null,getHead());
            } else {
                /*把当前结点添加到尾部*/
                if (compareAndSetTail(t,node)) {
                    t.next = node;
                    node.pre = t;
                    return t;
                }
            }
        }
    }

    public int unlock(){
        return 1;
    }





    private final boolean compareAndSetHead(AqsStudy.Node update) {
        return UNSAFE.compareAndSwapObject(this, headOffset, null, update);
    }

    private final boolean compareAndSetTail(AqsStudy.Node expect, AqsStudy.Node update) {
        return UNSAFE.compareAndSwapObject(this, tailOffset, expect, update);
    }




    private class Node {

        private int lockType;
        private Thread t;
        private int status;
        private Node pre;
        private Node next;

        public Node() {
        }

        public Node(Thread currentThread, int i) {
            this.lockType = i;
            this.t = currentThread;
        }

        public Node getPre() {
            return pre;
        }

        public void setPre(Node pre) {
            this.pre = pre;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }

        public int getLockType() {
            return lockType;
        }

        public void setLockType(int lockType) {
            this.lockType = lockType;
        }

        public Thread getT() {
            return t;
        }

        public void setT(Thread t) {
            this.t = t;
        }

        public int getStatus() {
            return status;
        }

        public void setStatus(int status) {
            this.status = status;
        }
    }

    private enum LockModel {
        MONOPOLY(1,"独占"),
        SHARED(0,"共享"),
        ;

        private int num;
        private String text;

        LockModel(int num, String text) {
            this.num = num;
            this.text = text;
        }

        public int getNum() {
            return num;
        }

        public void setNum(int num) {
            this.num = num;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }
    }

    private enum Status {
        SIGNAL(-1,"等待"),
        INTERRUPT(0,"中断"),
        CANCEL(1,"取消"),
        ;

        private int num;
        private String text;

        Status(int num, String text) {
            this.num = num;
            this.text = text;
        }

        public int getNum() {
            return num;
        }

        public void setNum(int num) {
            this.num = num;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }
    }
}
